package server

import (
	"bytes"
	"context"
	"crypto/md5"
	"crypto/tls"
	"crypto/x509"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"go-papers/pkg/lognew"
)

var _tlsConfig *tls.Config

var (
	wechatUrl                  = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack" //请求发红包的地址
	formatDate                 = "20060102"
	xmlStr                     = "xml"
	RedPacketRequestStr        = "RedPacketRequest"
	weixinPayKey               = "19200bbb0b4c09247ec02edce69f6ddd" //微信密钥
	weixinMchId                = "10000098"                         //微信商户号
	strWeixinAppId             = "wx8888888888888888"               //公众账号ID
	strWeixinClientCertPemPath = "/home/apiclient_cert.pem"         //客户端证书存放绝对路径
	strWeixinClientKeyPemPath  = "/home/apiclient_key.pem"          //客户端私匙存放绝对路径
	strWeixinRootCaPath        = "/home/rootca.pem"                 //服务端证书存放绝对路径
)

type WeixinSendRedPacket struct {
}

//发红包的请求实体
type RedPacketRequest struct {
	ActName     string `xml:"act_name"`     //必填，活动名称
	ClientIp    string `xml:"client_ip"`    //必填，调用接口的机器ip地址
	MchBillno   string `xml:"mch_billno"`   //必填，商户订单号
	MchId       string `xml:"mch_id"`       //必填，微信支付分配的商户号
	NonceStr    string `xml:"nonce_str"`    //必填,随机字符串，不超过32位
	ReOpenid    string `xml:"re_openid"`    //必填，接收红包者用户，用户在wxappid下的openid
	Remark      string `xml:"remark"`       //必填，备注信息
	SendName    string `xml:"send_name"`    //必填，红包发送者名称
	TotalAmount int    `xml:"total_amount"` //必填，付款金额，单位为分
	TotalNum    int    `xml:"total_num"`    //必填，红包发放人数
	Wishing     string `xml:"wishing"`      //必填，红包祝福语
	Wxappid     string `xml:"wxappid"`      //必填，微信公众号id
	Sign        string `xml:"sign"`         //必填，签名
	//SceneId		string		`xml:"scene_id"`	  //非必填，红包使用场景
	//RiskInfo 	string		`xml:"risk_info"`    //非必填，用户操作的时间戳
	//ConsumeMchId string		`xml:"consume_mch_id"` //非必填，资金授权商户号
}

//发微信红包
func (*WeixinSendRedPacket) SendRedPack(redPacketEntity *RedPacketRequest) (*http.Response, error) {
	nonce_str, _ := getUUID() //随机字符串

	//订单号,随机生成
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	mch_billno := weixinMchId + time.Now().Format(formatDate) + strconv.FormatInt(time.Now().Unix(), 10)[4:] + strconv.Itoa(r.Intn(8999)+1000)

	redPacketEntity.NonceStr = nonce_str
	redPacketEntity.MchBillno = mch_billno
	redPacketEntity.MchId = weixinMchId
	redPacketEntity.Wxappid = strWeixinAppId
	// 生成签名
	sign := strings.ToUpper(signature(*redPacketEntity)) //签名
	redPacketEntity.Sign = sign

	data, err := xml.MarshalIndent(redPacketEntity, "", "   ")

	if err != nil {
		lognew.Error(context.TODO(), "异常", lognew.Fields{"err": err})
		return nil, err
	}
	sendData := strings.Replace(string(data), RedPacketRequestStr, xmlStr, -1)

	fmt.Println(sendData)
	//POST数据
	return securePost(wechatUrl, []byte(sendData))
}

//加载微信发红包需要的证书
func getTLSConfig() (*tls.Config, error) {
	if _tlsConfig != nil {
		return _tlsConfig, nil
	}

	// load cert
	cert, err := tls.LoadX509KeyPair(strWeixinClientCertPemPath, strWeixinClientKeyPemPath)
	if err != nil {
		fmt.Println("load wechat keys fail", err)
		return nil, err
	}

	// load root ca
	caData, err := ioutil.ReadFile(strWeixinRootCaPath)
	if err != nil {
		fmt.Println("read wechat ca fail", err)
		return nil, err
	}
	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(caData)

	_tlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      pool,
	}
	return _tlsConfig, nil
}

//http发送请求
func securePost(url string, xmlContent []byte) (*http.Response, error) {
	tlsConfig, err := getTLSConfig()
	if err != nil {
		return nil, err
	}

	tr := &http.Transport{TLSClientConfig: tlsConfig}
	client := &http.Client{Transport: tr}

	return client.Post(
		url,
		"text/xml",
		bytes.NewBuffer(xmlContent))
}

//签名算法
func signature(sendParamEntity interface{}) string {
	str := getFieldString(sendParamEntity)
	if str != "" {
		str = fmt.Sprintf("%s&%s=%s", str, "key", weixinPayKey)
		fmt.Println(str)
		md5Ctx2 := md5.New()
		md5Ctx2.Write([]byte(str))
		str = hex.EncodeToString(md5Ctx2.Sum(nil))

		return str
	} else {
		return ""
	}
}

//获取结构体字段及值的拼接值
func getFieldString(sendParamEntity interface{}) string {
	m := reflect.TypeOf(sendParamEntity)
	v := reflect.ValueOf(sendParamEntity)
	var tagName string
	numField := m.NumField()
	w := make([]string, numField)
	numFieldCount := 0
	for i := 0; i < numField; i++ {
		fieldName := m.Field(i).Name
		tags := strings.Split(string(m.Field(i).Tag), "\"")
		if len(tags) > 1 {
			tagName = tags[1]
		} else {
			tagName = m.Field(i).Name
		}

		fieldValue := v.FieldByName(fieldName).Interface()

		if fieldValue != "" {
			s := fmt.Sprintf("%s=%v", tagName, fieldValue)
			w[numFieldCount] = s
			numFieldCount++
		}
	}
	if numFieldCount == 0 {
		return ""
	}
	w = w[:numFieldCount]
	sort.Strings(w)
	return strings.Join(w, "&")
}

//获取uuid
func getUUID() (string, error) {
	uuid := make([]byte, 16)
	n, err := rand.Read(uuid)
	if n != len(uuid) || err != nil {
		return "", err
	}
	uuid[8] = 0x80 // variant bits see page 5
	uuid[4] = 0x40 // version 4 Pseudo Random, see page 7

	return hex.EncodeToString(uuid), nil
}
